/*
 * @Author: LVGRAPE
 * @Date: 2022-05-23 17:13:09
 * @LastEditTime: 2023-09-08 14:53:23
 * @LastEditors: LVGRAPE
 * @Description: 用固定的左移和机取样序列来加密一段数据，并生成5个字节的特征码，从MD5魔改来的
 * 可以输入预定的版权声明、个性签名、空行等
 */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "encryption.h"
#include "zino.h"
#include <rtthread.h>

 /**4组特征计算函数 */
static inline uint8_t F(uint8_t x, uint8_t y, uint8_t z) { return ((x) & (y)) | ((~x) & (z)); };
static inline uint8_t G(uint8_t x, uint8_t y, uint8_t z) { return ((x) & (z)) | ((y) & (~z)); };
static inline uint8_t H(uint8_t x, uint8_t y, uint8_t z) { return (x) ^ (y) ^ (z); };
static inline uint8_t I(uint8_t x, uint8_t y, uint8_t z) { return (y) ^ ((x) | (~(z))); };
/**左移函数 */
static inline uint8_t ROTATELEFT(uint8_t num, uint8_t n) { return (num << n) | (num >> (8 - n)); }

/**4组特征更新函数 */
static inline uint8_t FF(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t x, uint8_t s, uint8_t ac)
{
    a += F(b, c, d) + x + ac;
    a = ROTATELEFT(a, s);
    a += b;
    return a;
}
static inline uint8_t GG(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t x, uint8_t s, uint8_t ac)
{
    a += G(b, c, d) + x + ac;
    a = ROTATELEFT(a, s);
    a += b;
    return a;
}
static inline uint8_t HH(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t x, uint8_t s, uint8_t ac)
{
    a += H(b, c, d) + x + ac;
    a = ROTATELEFT(a, s);
    a += b;
    return a;
}
static inline uint8_t II(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t x, uint8_t s, uint8_t ac)
{
    a += I(b, c, d) + x + ac;
    a = ROTATELEFT(a, s);
    a += b;
    return a;
}
typedef uint8_t(*calarry_t)(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t x, uint8_t s, uint8_t ac);
const static calarry_t calarry[4] = { FF, GG, HH, II };
/**
 * @brief 用随机左移和随机取样来加密一段数据，并生成5个字节的特征码
 *
 * @param data 数据输入
 * @return uint8_t* 返回特征码地址，5个有效数据
 */
char* randomEncrypt(const char* data)
{
    uint32_t dlen = strlen(data);
    uint32_t cdlen = (dlen % 5) ? (dlen + dlen / 5 + 5 - dlen % 5) : dlen; //计算要加密的长度，必须是5的整数倍，不是就补到5的倍数
    char* newData = calloc(cdlen, 1);                                      //内存申请
    newData = (char*)data;
    static char seed[5] = { 0x12, 0x34, 0x56, 0x78, 0x9a }; // 5个初始种子字节
    if (dlen % 5)                                         //不是5倍数长度时做填充
    {
        //剩下长度填充1000000...
        newData[dlen] = 0x80;
        memset(&newData[dlen + 1], 0, (cdlen - dlen - 1));
    }
    const uint8_t rotatArray[5][5] = {//随机循环左移-位数表(不要大于8，不然数据容易变成0，没有意义了)
                                   {7, 4, 3, 2, 1},
                                   {5, 1, 4, 0, 3},
                                   {4, 1, 6, 3, 2},
                                   {6, 0, 5, 1, 4},
                                   {6, 0, 5, 1, 7} };
    const uint8_t posArray[5][5] = {
        //取样表，不能大于4，不然会内存溢出崩溃
        {0, 1, 2, 3, 4},
        {1, 2, 3, 4, 0},
        {4, 3, 2, 1, 0},
        {3, 2, 1, 4, 0},
        {2, 3, 1, 4, 0},
    };
    for (char* p = newData; p < (newData + cdlen); p += 5) //每个5字节计算更新一次种子
    {
        for (uint8_t i = 0; i < 5; i++) //随机5个左移和5个取样
        {
            for (uint8_t j = 0; j < 5; j++) // 5个字节计算
            {
                for (uint8_t k = 0; k < 4; k++) // 4个加密计算公式，计算更新5个种子
                {
                    seed[0] = calarry[k](seed[1], seed[2], seed[3], seed[4], p[posArray[j][i]], rotatArray[j][i], seed[0]);
                    seed[1] = calarry[k](seed[2], seed[3], seed[4], seed[0], p[posArray[j][i]], rotatArray[j][i], seed[1]);
                    seed[2] = calarry[k](seed[3], seed[4], seed[0], seed[1], p[posArray[j][i]], rotatArray[j][i], seed[2]);
                    seed[3] = calarry[k](seed[4], seed[0], seed[1], seed[2], p[posArray[j][i]], rotatArray[j][i], seed[3]);
                    seed[4] = calarry[k](seed[0], seed[1], seed[2], seed[3], p[posArray[j][i]], rotatArray[j][i], seed[4]);
                }
            }
        }
    }
    return seed;
}
int encrypt(int args, char* argv[])
{
    for (int i = 1; i < args; i++)
    {
        char* seed = randomEncrypt(argv[i]);
        rt_kprintf("encrypt[%d]:%02X,%02X,%02X,%02X,%02X\n", i, seed[0],seed[1],seed[2],seed[3],seed[4]);
    }
    return 0;
}
//FIXME 加每次加密结果都不一样...
MSH_CMD_EXPORT(encrypt, random encrypt);